package com.brzjomo.embraceofthevoid.items;

import com.brzjomo.embraceofthevoid.EmbraceOfTheVoid;
import com.brzjomo.embraceofthevoid.init.ModItems;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.World;

import java.util.Objects;

public class BlessTeleportGem extends NightFallTeleportGem {
    public BlessTeleportGem(Settings settings) {
        super(settings);
    }

    private static boolean isPlayerInDanger(PlayerEntity player) {
        float maxHealth = 0;
        float currentHealth = 0;
        float triggerPercent = 0.15F;

        // get player max health
        if (!player.isSpectator() && player.isAlive()) {
            maxHealth = player.getMaxHealth();
            currentHealth = player.getHealth();
        }

        // judge if player is in danger
        return currentHealth != 0 && currentHealth <= maxHealth * triggerPercent;
    }

    public static boolean isTargetUnProper(World world, BlockPos targetBlockPosition) {
        Block targetBlock = world.getBlockState(new BlockPos(targetBlockPosition.getX(), targetBlockPosition.getY(), targetBlockPosition.getZ())).getBlock();
        Block targetUpBlock = world.getBlockState(new BlockPos(targetBlockPosition.getX(), targetBlockPosition.getY() + 1, targetBlockPosition.getZ())).getBlock();
        Block targetDownBlock = world.getBlockState(new BlockPos(targetBlockPosition.getX(), targetBlockPosition.getY() - 1, targetBlockPosition.getZ())).getBlock();

        boolean isTargetBlockLava = targetBlock.equals(Blocks.LAVA);
        boolean isTargetBlockWater = targetBlock.equals(Blocks.WATER);
        boolean isTargetBlockBedrock = targetBlock.equals(Blocks.BEDROCK);
        boolean isTargetUpBlockLava = targetUpBlock.equals(Blocks.LAVA);
        boolean isTargetUpBlockWater = targetUpBlock.equals(Blocks.WATER);
        boolean isTargetUpBlockBedrock = targetUpBlock.equals(Blocks.BEDROCK);
        boolean isTargetDownBlockLava = targetDownBlock.equals(Blocks.LAVA);
        boolean isTargetDownBlockWater = targetDownBlock.equals(Blocks.WATER);
        boolean isTargetDownBlockBedrock = targetDownBlock.equals(Blocks.BEDROCK);

        return isTargetBlockLava || isTargetBlockWater || isTargetBlockBedrock || isTargetUpBlockLava || isTargetUpBlockWater || isTargetUpBlockBedrock || isTargetDownBlockLava || isTargetDownBlockWater || isTargetDownBlockBedrock;
    }

    public static boolean isTargetInVoid(World world, BlockPos targetBlockPosition) {
        Block targetDownBlock = world.getBlockState(new BlockPos(targetBlockPosition.getX(), targetBlockPosition.getY() - 1, targetBlockPosition.getZ())).getBlock();

        return targetDownBlock.equals(Blocks.VOID_AIR);
    }

    public static BlockPos getStandAblePosition(World world, BlockPos blockPos) {
        int posX = blockPos.getX();
        int posY = blockPos.getY();
        int posZ = blockPos.getZ();
        Block targetBlock = world.getBlockState(new BlockPos(posX, posY, posZ)).getBlock();
        Block targetUpBlock = world.getBlockState(new BlockPos(posX, posY + 1, posZ)).getBlock();


        while (!targetBlock.equals(Blocks.AIR) || (targetBlock.equals(Blocks.AIR) && !targetUpBlock.equals(Blocks.AIR))) {
            posY += 1;
            targetBlock = world.getBlockState(new BlockPos(posX, posY, posZ)).getBlock();
            targetUpBlock = world.getBlockState(new BlockPos(posX, posY + 1, posZ)).getBlock();
        }

        return new BlockPos(posX, posY, posZ);
    }

    @Override
    public Text getName() {
        return Text.translatable(this.getTranslationKey());
    }

    @Override
    public Text getName(ItemStack stack) {
        return Text.translatable(this.getTranslationKey(stack));
    }

    @Override
    public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) {
        NbtCompound nbt = stack.getOrCreateNbt();
        boolean teleportRandomly = true;

        if (stack.isOf(ModItems.BLESS_TELEPORT_GEM)) {
            if (!nbt.contains("particleTickCount")) {
                nbt.putInt("particleTickCount", 35);
            } else {
                nbt.putInt("particleTickCount", nbt.getInt("particleTickCount") + 1);

                if (nbt.getInt("particleTickCount") > 100) {
                    nbt.putInt("particleTickCount", 35);
                }
            }
        }

        if (entity.isPlayer()) {
            PlayerEntity player = (PlayerEntity) entity;
            PlayerInventory playerInventory = player.getInventory();
            DefaultedList<ItemStack> playerInventoryList = playerInventory.main;

            // decide which gem should active
            if (stack.isOf(ModItems.BLESS_TELEPORT_GEM)) {
                ItemStack mainHandStack = player.getMainHandStack();
                ItemStack offHandStack = player.getOffHandStack();
                NbtCompound mainHandNBT = mainHandStack.getOrCreateNbt();
                NbtCompound offHandNBT = offHandStack.getOrCreateNbt();
                boolean isBlessTeleportGemInMainHand = mainHandStack.isOf(ModItems.BLESS_TELEPORT_GEM);
                boolean isBlessTeleportGemInOffHand = offHandStack.isOf(ModItems.BLESS_TELEPORT_GEM);
                boolean isCoolingDown = player.getItemCooldownManager().isCoolingDown(this);

                if (!isPlayerInDanger(player)) {
                    nbt.putBoolean("hasTeleported", false);
                }

                // init new gem
                if (!nbt.contains("hasTeleported")) {
                    nbt.putBoolean("hasTeleported", false);
                }

                if (selected && isPlayerInDanger(player) && !nbt.getBoolean("hasTeleported") && !isCoolingDown) {
                    mainHandNBT.putBoolean("shouldActive", true);

                    if (mainHandNBT.contains("PinnedPos_X")) {
                        teleportRandomly = false;
                    }
                } else if (slot == 0 && isBlessTeleportGemInOffHand && !isBlessTeleportGemInMainHand && isPlayerInDanger(player) && !nbt.getBoolean("hasTeleported") && !isCoolingDown) {
                    offHandNBT.putBoolean("shouldActive", true);

                    if (offHandNBT.contains("PinnedPos_X")) {
                        teleportRandomly = false;
                    }
                } else if (!isBlessTeleportGemInMainHand && !isBlessTeleportGemInOffHand && isPlayerInDanger(player) && !nbt.getBoolean("hasTeleported") && !isCoolingDown) {
                    boolean isValidGemFound = false;

                    // judge which gem in inventory should active
                    // find a valid gem which has a position
                    for (int i = 0; i < PlayerInventory.MAIN_SIZE; i++) {
                        ItemStack targetItem = playerInventoryList.get(i);
                        if (!targetItem.isEmpty() && targetItem.isOf(ModItems.BLESS_TELEPORT_GEM)) {
                            if (targetItem.getOrCreateNbt().contains("PinnedPos_X")) {
                                if (slot == i) {
                                    isValidGemFound = true;
                                    teleportRandomly = false;
                                    targetItem.getOrCreateNbt().putBoolean("shouldActive", true);
                                    break;
                                } else if (nbt.contains("shouldActive")) {
                                    nbt.putBoolean("shouldActive", false);
                                }
                                break;
                            }
                        }
                    }

                    // try to find a gem has no position instead
                    if (!isValidGemFound) {
                        for (int i = 0; i < PlayerInventory.MAIN_SIZE; i++) {
                            ItemStack targetItem = playerInventoryList.get(i);
                            if (!targetItem.isEmpty() && targetItem.isOf(ModItems.BLESS_TELEPORT_GEM)) {
                                if (slot == i) {
                                    targetItem.getOrCreateNbt().putBoolean("shouldActive", true);
                                    break;
                                } else if (nbt.contains("shouldActive")) {
                                    nbt.putBoolean("shouldActive", false);
                                }
                                break;
                            }
                        }
                    }
                } else {
                    nbt.putBoolean("shouldActive", false);
                }
            }

            // active & teleport
            if (nbt.getBoolean("shouldActive")) {
                // server: teleport player
                if (!world.isClient()) {
                    if (!teleportRandomly) {
                        // in same world
                        if (isSameWorld(nbt, player)) {
                            BlockPos pinnedPosition = readPositionFromNbt(nbt);
                            this.logPlayerFromAndGo(player, pinnedPosition.getX() + 0.5D, pinnedPosition.getY() + 1.0D, pinnedPosition.getZ() + 0.5D, this.getPlayerWorldInfo(player), this.getDestinationWorldInfo(nbt));
                            player.requestTeleport(pinnedPosition.getX() + 0.5D, pinnedPosition.getY() + 1.0D, pinnedPosition.getZ() + 0.5D);
                        }

                        // in different worlds
                        if (!isSameWorld(nbt, player)) {
                            String pinnedDimension = nbt.getString("PinnedWorldRegistry");
                            Identifier dimensionID = new Identifier(this.getWorldNameSpace(pinnedDimension), this.getWorldID(pinnedDimension));

                            RegistryKey<World> worldRegistryKey = RegistryKey.of(RegistryKeys.WORLD, dimensionID);
                            ServerWorld serverWorld = Objects.requireNonNull(world.getServer()).getWorld(worldRegistryKey);

                            if (player.lastNetherPortalPosition == null) {
                                player.lastNetherPortalPosition = new BlockPos(0, 0, 0);
                            }

                            BlockPos pinnedPosition = readPositionFromNbt(nbt);
                            this.logPlayerFromAndGo(player, pinnedPosition.getX() + 0.5D, pinnedPosition.getY() + 1.0D, pinnedPosition.getZ() + 0.5D, this.getPlayerWorldInfo(player), this.getDestinationWorldInfo(nbt));
                            player.moveToWorld(serverWorld);
                            player.requestTeleport(pinnedPosition.getX() + 0.5D, pinnedPosition.getY() + 1.0D, pinnedPosition.getZ() + 0.5D);
                        }
                    } else {
                        String currentDimension = player.getWorld().getRegistryKey().toString();
                        BlockPos startPosition = player.getBlockPos();
                        int targetX = 0, targetY = 0, targetZ = 0;
                        BlockPos targetPosition = new BlockPos(targetX, targetY, targetZ);

                        // debug
                        if (EmbraceOfTheVoid.IS_DEBUG) {
                            EmbraceOfTheVoid.LOGGER.info("Dim: " + currentDimension);
                        }

                        // randomly teleport player
                        if (currentDimension.contains("overworld")) {
                            while (targetPosition.equals(new BlockPos(0, 0, 0)) || isTargetUnProper(world, targetPosition)) {
                                targetX = startPosition.getX() + Random.create().nextBetween(-800, 800);
                                targetZ = startPosition.getZ() + Random.create().nextBetween(-800, 800);
                                targetPosition = new BlockPos(targetX, targetY, targetZ);

                                targetPosition = getStandAblePosition(world, targetPosition);
                            }
                        } else if (currentDimension.contains("the_nether")) {
                            while (targetPosition.equals(new BlockPos(0, 0, 0)) || isTargetUnProper(world, targetPosition)) {
                                targetX = startPosition.getX() + Random.create().nextBetween(-200, 200);
                                targetZ = startPosition.getZ() + Random.create().nextBetween(-200, 200);
                                targetPosition = new BlockPos(targetX, targetY, targetZ);

                                targetPosition = getStandAblePosition(world, targetPosition);
                            }
                        } else if (currentDimension.contains("the_end")) {
                            while (targetPosition.equals(new BlockPos(0, 0, 0)) || isTargetInVoid(world, targetPosition)) {
                                targetX = startPosition.getX() + Random.create().nextBetween(-100, 100);
                                targetZ = startPosition.getZ() + Random.create().nextBetween(-100, 100);
                                targetPosition = new BlockPos(targetX, targetY, targetZ);

                                targetPosition = getStandAblePosition(world, targetPosition);
                            }
                        } else {
                            while (targetPosition.equals(new BlockPos(0, 0, 0)) || isTargetUnProper(world, targetPosition)) {
                                targetX = startPosition.getX() + Random.create().nextBetween(-500, 500);
                                targetZ = startPosition.getZ() + Random.create().nextBetween(-500, 500);
                                targetPosition = new BlockPos(targetX, targetY, targetZ);

                                targetPosition = getStandAblePosition(world, targetPosition);
                            }
                        }

                        this.logPlayerFromAndGo(player, targetPosition.getX() + 0.5D, targetPosition.getY() + 1.0D, targetPosition.getZ() + 0.5D, this.getPlayerWorldInfo(player), this.getDestinationWorldInfo(nbt, player));
                        player.requestTeleport(targetPosition.getX() + 0.5D, targetPosition.getY() + 1.0D, targetPosition.getZ() + 0.5D);

                        // prevent player falling to death
                        player.fallDistance = 0;
                    }

                    // set cool down
                    player.getItemCooldownManager().set(this, 100);

                    // damage item
                    int maxDamage = stack.getMaxDamage();
                    if (nbt.getBoolean("shouldActive")) {
                        nbt.putInt("Damage", nbt.getInt("Damage") + 1);
                    }

                    // update NBT
                    nbt.putBoolean("shouldActive", false);
                    nbt.putBoolean("hasTeleported", true);

                    // stop any other gem to teleport
                    for (int i = 0; i < PlayerInventory.MAIN_SIZE; i++) {
                        ItemStack targetItem = playerInventoryList.get(i);
                        if (!targetItem.isEmpty() && targetItem.isOf(ModItems.BLESS_TELEPORT_GEM)) {
                            targetItem.getOrCreateNbt().putBoolean("hasTeleported", true);
                        }
                    }

                    // decrease item
                    if (nbt.getInt("Damage") >= maxDamage) {
                        stack.decrement(1);
                    }

                    // put out player's fire
                    if (player.isOnFire()) {
                        player.setFireTicks(-60);
                    }
                }

                // play sounds
                world.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.PLAYERS, 1.0F, 0.4F);

                // trigger particle
                nbt.putInt("particleTickCount", 0);
            }
        }

        // add particle
        if (nbt.getInt("particleTickCount") < 35) {
            world.addParticle(ParticleTypes.PORTAL, entity.getX(), entity.getBodyY(0.5D), entity.getZ(), (Random.create().nextDouble() - 0.5D) * 2, -Random.create().nextDouble(), (Random.create().nextDouble() - 0.5D) * 2);
        }

        super.inventoryTick(stack, world, entity, slot, selected);
    }
}
